import {
  Component,
  OnInit,
  ViewEncapsulation,
  ChangeDetectionStrategy,
  Input,
  Output,
  EventEmitter,
  AfterViewInit,
  OnDestroy,
  QueryList,
  ContentChildren,
  forwardRef,
  ViewContainerRef,
  ViewChild,
  TemplateRef,
  ElementRef,
  ChangeDetectorRef,
  OnChanges,
  SimpleChanges,
  Renderer2,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Subject } from 'rxjs';
import { SelectionModel } from '@angular/cdk/collections';
import { Overlay, OverlayConfig } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal';
import { findIndex as _findIndex, isNil as _isNil } from 'lodash';
import { takeUntil, filter, tap } from 'rxjs/operators';
import { InputBoolean, LvSizeLSD, LvSelectSM, OverlayService, I18NService } from '../core/api';
import { SelectService } from './select.service';
import { OptionComponent } from './option.component';

@Component({
  selector: 'lv-selection',
  templateUrl: './selection.component.html',
  styles: [],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef((): typeof SelectionComponent => SelectionComponent),
      multi: true,
    },
    SelectService,
    OverlayService,
  ],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: {
    class: 'lv-select',
    '[class.lv-select-size-large]': "lvSize === 'large'",
    '[class.lv-select-size-default]': "lvSize === 'default'",
    '[class.lv-select-size-small]': "lvSize === 'small'",
    '[class.disabled]': 'lvDisabled',
    '[class.lv-select-open]': 'selectOpen',
    '(click)': '_toggleSelect($event)',
  },
})
export class SelectionComponent implements ControlValueAccessor, OnInit, AfterViewInit, OnDestroy, OnChanges {
  // 输入输出属性
  @Input() lvOptions: any[] = []; // 暂时数据：下拉列表
  @Input() lvSize: LvSizeLSD = 'default';
  @Input() @InputBoolean() lvLoading: boolean = false; // 是否加载中
  @Input() @InputBoolean() lvDisabled: boolean = false; // 是否禁用
  @Output() lvOpenChange = new EventEmitter<boolean>(); // 切换选择下拉回调事件
  @Input() @InputBoolean() lvShowClear: boolean = false; // 是否显示清除按钮
  @Input() lvCompareWith: (o1: any, o2: any) => boolean; // 用于将选项值与所选值进行比较的函数。第一个参数是选项中的值。第二个是选择的值。应该返回一个布尔值。
  @Output() lvSearchChange = new EventEmitter<string>(); // 搜索文本框变化时回调事件
  @Output() lvSelectionChange = new EventEmitter<any>(); // 选中值变化时回调事件
  @Input() @InputBoolean() lvShowSearch: boolean = false; // 是否可以搜索

  @Input() lvOptionTemplate: TemplateRef<{ $implicit: any }>; // 自定义下拉
  @Input() lvSelectTriggerTemplate: TemplateRef<{ $implicit: any }>; // 自定义展示区

  @Input() lvPlaceholder: string = this.i18n.get('selectPlaceholder'); // 如果没有选择任何值，显示的占位符
  @ContentChildren(OptionComponent) _options: QueryList<OptionComponent>;
  @ViewChild('normalPanel', { static: false }) _normalPanel: ElementRef<any>;

  @ViewChild('selectWrapper', { static: false }) _selectWrapper: ElementRef<any>;
  @Input() lvPanelClass: string | string[] | Set<string> | { [key: string]: any }; // 设置下拉面板的class同ngClass语法
  @ViewChild('normalOptionsTemplate', { static: false })
  _normalOptionsTemplate: TemplateRef<any>;

  value: any;
  overlayRef;

  selectOpen: boolean = false;

  // 外部可访问变量
  multiple = this._selectService.isMultipleMode();
  templateMap: { [key: string]: TemplateRef<any> } = {};
  private _activeElement: any;
  private _selectWidth: number;
  // 内部变量
  private _destroy$ = new Subject<void>();
  private _selection: SelectionModel<any>;
  private _selectToggle$ = new Subject<boolean>();
  constructor(
    private _selectService: SelectService,
    private viewContainerRef: ViewContainerRef,
    private overlayService: OverlayService,
    private overlay: Overlay,
    private elementRef: ElementRef,
    private _cdr: ChangeDetectorRef,
    private i18n: I18NService,
    private renderer: Renderer2,
  ) {
    this._activeElement = elementRef.nativeElement;
  }

  // 空
  get empty(): boolean {
    // 是否有选中项
    return _isNil(this.value);
  }

  @Input() set lvMode(value: LvSelectSM) {
    // 下拉模式：单选、多选
    this._selectService.setMode(value);
  }

  ngOnInit(): void {
    this.initData();
  }

  setDisabledState?(): void {}

  registerOnChange(fn: any): void {
    this._onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this._onTouch = fn;
  }

  /**
   * 1. 订阅selection open
   */
  ngAfterViewInit(): void {
    // 1
    this._subscribeOpenChange();
  }

  writeValue(value: any): void {
    this.value = value;
    this._cdr.markForCheck();
  }

  setTemplateMap(source): void {
    this.templateMap = { ...this.templateMap, ...source };
  }

  clearOverlayRef(): void {
    // 关闭下拉
    if (this._existOverlayRef()) {
      this.overlayRef.close();
    }
  }

  _existOverlayRef(): any {
    return this.overlayRef?.overlayRef.hasAttached();
  }

  initData(): void {
    // 初始化选中项
    this._selectService.initSelection(this.multiple, this.value);
  }

  clearSelected(e: MouseEvent): void {
    e.stopPropagation();
    this.selectOption(undefined, false);
  }

  ngOnDestroy(): void {
    this._destroy$.next();
    this._destroy$.complete();
    this.clearOverlayRef();
  }

  selectOption(value: any, close: boolean = true): void {
    this.writeValue(value);
    this._onChange(value);
    if (close) {
      this.clearOverlayRef();
    }
  }

  _toggleSelect(e: MouseEvent): void {
    if (this.lvDisabled) {
      e.preventDefault();
      return;
    }
    this._selectToggle$.next(!this.selectOpen);
  }

  ngOnChanges(changes: SimpleChanges): void {
    const triggerChange = changes.lvSelectTriggerTemplate;
    const optionChange = changes.lvOptionTemplate;
    if (triggerChange || optionChange) {
      this.setTemplateMap({
        selectTrigger: triggerChange?.currentValue,
        option: optionChange?.currentValue,
      });
    }
  }

  _subscribeOpenChange(): void {
    this._selectToggle$
      .pipe(
        tap((res): void => {
          this.selectOpen = res;
          if (!res) {
            this.clearOverlayRef();
            this._cdr.markForCheck();
          }
          this.lvOpenChange.emit(res);
        }), // 关闭
        filter((res): any => res), // 打开
        takeUntil(this._destroy$),
      )
      .subscribe((): void => {
        this._openPanelOverlay();
      });
  }

  setPanelHighlightPosition(panelDom?: any): void {
    const index = _findIndex(this.lvOptions, (item): boolean => item === this.value);
    const itemClass = 'lv-option';
    const optionsDom = panelDom ? panelDom : this._normalPanel.nativeElement;
    const itemDom = optionsDom.querySelector(`.${itemClass}`);

    // 没有下拉选项时
    if (!itemDom) {
      return;
    }
    const optionsHeight = optionsDom.offsetHeight;
    const itemHeight = itemDom.clientHeight;
    const maxHeight = 200;
    if (index < 0 || optionsHeight < maxHeight) {
      return;
    }
    optionsDom.scrollTop = (index + 1) * itemHeight - optionsHeight / 2;
  }

  _openPanelOverlay(): void {
    this._selectWidth = this._activeElement.clientWidth;
    if (!this._existOverlayRef()) {
      this.selectOpen = true;
      const config = new OverlayConfig({
        minWidth: this._selectWidth,
        hasBackdrop: false,
        positionStrategy: this.overlay
          .position()
          .flexibleConnectedTo(this._selectWrapper)
          .withPositions([
            {
              originX: 'start',
              originY: 'bottom',
              overlayX: 'start',
              overlayY: 'top',
              offsetY: -1,
            },
            {
              originX: 'start',
              originY: 'top',
              overlayX: 'start',
              overlayY: 'bottom',
              offsetY: 1,
            },
          ]),
      });
      const extendConfig = {
        backdropBindClose: true,
        afterOpen: (overlayRef: any, res: any): void => {
          // 设置z-index
          this.renderer.addClass(overlayRef.overlayRef.hostElement, 'lv-select-panel-z-index');

          this.setPanelHighlightPosition(res.rootNodes[0].querySelector('.lv-select-panel-content'));
        },
        afterClose: (): void => {
          this._selectToggle$.next(false);
        },
      };

      // 处理没点按钮 下拉消失选中状态不对应问题
      const portal = new TemplatePortal(this._normalOptionsTemplate, this.viewContainerRef, {
        $implicit: this.lvOptions,
      });
      this.overlayRef = this.overlayService.open(portal, config, extendConfig);
      this._cdr.markForCheck();
    }
  }

  private _onTouch: any = (): void => {};

  private _onChange: any = (): void => {};
}
